﻿using System;
using System.Collections.Generic;
using System.Text;

namespace SourcePacker
{
    class Compactor
    {
        enum State
        {
            Free,
            InChar,
            InString,
            InComment,
            AfterStarInComment,
            InLineComment,
            InSpace,
            InLineBreak,
            AfterForwardSlash,
            AfterBackSlash,
            AfterBackSlashInChar,
            AfterBackSlashInString,
        };
        
        public Compactor(string input)
        {
            buffer = new StringBuilder();
            state = State.InLineBreak;

            foreach (char c in input)
                next(c);

            next('\n');
            Result = buffer.ToString();
        }

        public string Result { get; private set; }

        State state;
        State stateBeforeComment;
        StringBuilder buffer;

        void next(char c)
        {
            switch (state)
            {
                case State.Free: onStateFree(c); break;
                case State.InChar: onStateInChar(c);break;
                case State.InString: onStateInString(c); break;
                case State.InComment: onStateInComment(c); break;
                case State.AfterStarInComment: onStateAfterStarInComment(c); break;
                case State.InLineComment: onStateInLineComment(c); break;
                case State.InSpace: onStateInSpace(c); break;
                case State.InLineBreak: onStateInLineBreak(c); break;
                case State.AfterForwardSlash: onStateAfterForwardSlash(c); break;
                case State.AfterBackSlash: onStateAfterBackSlash(c); break;
                case State.AfterBackSlashInChar: onStateAfterBackSlashInChar(c); break;
                case State.AfterBackSlashInString: onStateAfterBackSlashInString(c); break;
            }
        }

        void onStateFree(char c)
        {
            switch (c)
            {
                case '/': stateBeforeComment = state; state = State.AfterForwardSlash; break;
                case '\\': state = State.AfterBackSlash; buffer.Append(c); break;
                case '\'': state = State.InChar; buffer.Append(c); break;
                case '\"': state = State.InString; buffer.Append(c); break;
                case ' ':
                case '\r':
                case '\t': state = State.InSpace; buffer.Append(' '); break;
                case '\n': state = State.InLineBreak; buffer.Append('\n'); break;
                default: buffer.Append(c); state = State.Free; break;
            }
        }

        void onStateInChar(char c)
        {
            switch (c)
            {
                case '\\': state = State.AfterBackSlashInChar; break;
                case '\'': state = State.Free; break;
                default: break;
            }

            buffer.Append(c);
        }

        void onStateInString(char c)
        {
            switch (c)
            {
                case '\\': state = State.AfterBackSlashInString; break;
                case '\"': state = State.Free; break;
                default: break;
            }

            buffer.Append(c);
        }

        void onStateInComment(char c)
        {
            switch (c)
            {
                case '*': state = State.AfterStarInComment; break;
                default: break;
            }
        }

        void onStateAfterStarInComment(char c)
        {
            switch (c)
            {
                case '*': state = State.AfterStarInComment; break;
                case '/': state = stateBeforeComment; break;
                default: state = State.InComment; break;
            }
        }

        void onStateInLineComment(char c)
        {
            switch (c)
            {
                case '\n': state = stateBeforeComment; next(c); break;
                default: break;
            }
        }

        void onStateInSpace(char c)
        {
            switch (c)
            {
                case ' ':
                case '\r':
                case '\t': break;
                default: onStateFree(c); break;
            }
        }

        void onStateInLineBreak(char c)
        {
            switch (c)
            {
                case ' ':
                case '\r':
                case '\t':
                case '\n': break;
                default: onStateFree(c); break;
            }
        }

        void onStateAfterForwardSlash(char c)
        {
            switch (c)
            {
                case '/': state = State.InLineComment; break;
                case '*': state = State.InComment; break;
                default: buffer.Append('/'); onStateFree(c); break;
            }
        }

        void onStateAfterBackSlash(char c)
        {
            switch (c)
            {
                default: buffer.Append(c); state = State.Free; break;
            }
        }

        void onStateAfterBackSlashInChar(char c)
        {
            switch (c)
            {
                default: buffer.Append(c); state = State.InChar; break;
            }
        }

        void onStateAfterBackSlashInString(char c)
        {
            switch (c)
            {
                default: buffer.Append(c); state = State.InString; break;
            }
        }
    }
}
